//
//  NSString+Digest.m
//  FashionTech
//
//  Created by JackieYip on 16/4/13.
//  Copyright © 2016年 mondial-d. All rights reserved.
//

#import "NSString+Digest.h"
#import <CommonCrypto/CommonCrypto.h>

static SecKeyRef rsaPublicKey = NULL;

SecKeyRef getRSAPublicKey()
{
    if (rsaPublicKey == NULL) {
        NSString *certPath = [[NSBundle mainBundle] pathForResource:@"cert" ofType:@"der"];
        SecCertificateRef myCertificate = nil;
        NSData *certificateData = [[NSData alloc] initWithContentsOfFile:certPath];
        myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
        SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
        SecTrustRef myTrust;
        OSStatus status = SecTrustCreateWithCertificates(myCertificate, myPolicy, &myTrust);
        SecTrustResultType trustResult;
        if (status == noErr)
            SecTrustEvaluate(myTrust, &trustResult);
        
        rsaPublicKey = SecTrustCopyPublicKey(myTrust);
        CFRelease(myPolicy);
        CFRelease(myCertificate);
    }
    
    return rsaPublicKey;
}

// ---------------------------------------------------------------------------------------------

@implementation NSString (Digest)

- (NSString *)RSAEncryptText:(NSString *)plainText key:(SecKeyRef)key
{
    size_t cipherBufferSize = SecKeyGetBlockSize(key);
    if (cipherBufferSize == 0)
        return nil;
    
    uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    if (cipherBuffer) {
        memset((void *)cipherBuffer, '\0', cipherBufferSize);
        
        NSData *plainTextBytes = [plainText dataUsingEncoding:NSUTF8StringEncoding];
        size_t blockSize = cipherBufferSize - 11;
        int numBlock = (int)ceilf(1.0f * plainTextBytes.length / blockSize);
        NSMutableData *encryptedData = [[NSMutableData alloc] init];
        for (int i=0; i<numBlock; i++) {
            size_t bufferSize = MIN(blockSize, plainTextBytes.length-i*blockSize);
            NSData *buffer = [plainTextBytes subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
            OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)buffer.bytes, buffer.length, cipherBuffer, &cipherBufferSize);
            if (status == noErr) {
                NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
                [encryptedData appendData:encryptedBytes];
            }
            else {
                free(cipherBuffer);
                return nil;
            }
        }
        
        free(cipherBuffer);
        NSString *encrypotoResult = [encryptedData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        return encrypotoResult;
    }
    
    return nil;
}

- (NSString *)sha1
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    
    return output;
}

- (NSString *)md5
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t digest[CC_MD5_DIGEST_LENGTH];
    CC_MD5(data.bytes, (CC_LONG)data.length, digest);
    NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    
    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    
    return output;
}

- (NSString *)sha512
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t digest[CC_SHA512_DIGEST_LENGTH];
    
    CC_SHA512(data.bytes, (CC_LONG)data.length, digest);
    NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];
    
    for (int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++) {
        [output appendFormat:@"%02x", digest[i]];
    }
    
    return output;
}

- (NSString *)sha512_base64
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t digest[CC_SHA512_DIGEST_LENGTH];
    CC_SHA512(data.bytes, (CC_LONG)data.length, digest);
    NSData *originData = [NSData dataWithBytes:digest length:CC_SHA512_DIGEST_LENGTH];
    NSString *encodedStr = [originData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    
    return encodedStr;
}

- (NSString *)base64Encode
{
    NSData *originData = [self dataUsingEncoding:NSASCIIStringEncoding];
    NSString *encodedStr = [originData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    
    return encodedStr;
}

- (NSString *)base64Decode
{
    NSData *originData = [[NSData alloc] initWithBase64EncodedString:self options:NSDataBase64DecodingIgnoreUnknownCharacters];
    NSString *decodedStr = [[NSString alloc] initWithData:originData encoding:NSASCIIStringEncoding];
    
    return decodedStr;
}

- (NSString *)rsa_base64
{
    SecKeyRef key = getRSAPublicKey();
    return [self RSAEncryptText:self key:key];
}

@end